package com.qqs.distributed.advice;

import com.qqs.distributed.annotation.CacheType;
import com.qqs.distributed.cache.CacheHandler;
import com.qqs.distributed.lock.LockHandler;
import com.qqs.distributed.lock.LockHandlerFactory;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.redisson.api.RBloomFilter;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


public abstract class AbstractCacheInterceptor implements MethodInterceptor, ApplicationContextAware {
    public static final RBloomFilter NULL_BLOOM_FILTER = new NullRBloomFilter();

    private ApplicationContext applicationContext;
    private CacheAttributeSource cacheAttributeSource;
    private Set<LockHandlerFactory> lockHandlerFactorySet = ConcurrentHashMap.newKeySet();
    private Set<CacheHandler> cacheHandlerSet = ConcurrentHashMap.newKeySet();
    private Map<String, RBloomFilter> bloomFilterMap = new ConcurrentHashMap();

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        CacheAttributeWrapper attributeWrapper = determineCacheAttributeWrapper(invocation);
        if (attributeWrapper == null) {
            return invocation.proceed();
        }
        LockHandler lockHandler = determineLockHandler(attributeWrapper);
        CacheHandler cacheHandler = determineCacheHandler(attributeWrapper.getAttribute().getCacheType());
        RBloomFilter bloomFilter = determineRBloomFilter(attributeWrapper.getAttribute());
        CacheHandlerAdapter handlerAdapter = determineCacheHandlerAdapter(lockHandler, cacheHandler, attributeWrapper, bloomFilter, invocation);
        return handlerAdapter.handle();
    }

    abstract CacheAttributeWrapper determineCacheAttributeWrapper(MethodInvocation invocation);

    abstract LockHandler determineLockHandler(CacheAttributeWrapper cacheAttributeWrapper);

    abstract CacheHandler determineCacheHandler(CacheType cacheType);

    protected RBloomFilter determineRBloomFilter(CacheAttribute attribute) {
        String bloom = attribute.getBloom();
        if (!StringUtils.hasLength(bloom)) {
            return NULL_BLOOM_FILTER;
        }

        Map<String, RBloomFilter> filterMap = getBloomFilterMap();
        RBloomFilter bloomFilter = filterMap.get(bloom);
        if (bloomFilter != null) {
            return bloomFilter;
        }
        synchronized (filterMap) {
            bloomFilter = filterMap.get(bloom);
            if (bloomFilter != null) {
                return bloomFilter;
            }
            ApplicationContext applicationContext = getApplicationContext();
            bloomFilter = applicationContext.getBean(bloom, RBloomFilter.class);
            if (bloomFilter == null) {
                bloomFilter = NULL_BLOOM_FILTER;
            }
            filterMap.put(bloom, bloomFilter);
            return bloomFilter;
        }
    }

    protected CacheHandlerAdapter determineCacheHandlerAdapter(LockHandler lockHandler, CacheHandler cacheHandler,
                                                               CacheAttributeWrapper attributeWrapper, RBloomFilter bloomFilter, MethodInvocation invocation) {
        if (lockHandler == LockHandler.SYNC_LOCK_HANDLER) {
            return new SyncCacheHandlerAdapter(cacheHandler, attributeWrapper, bloomFilter, invocation);
        }
        return new DefaultCacheHandlerAdapter(lockHandler, cacheHandler, attributeWrapper, bloomFilter, invocation);
    }

    public CacheAttributeSource getCacheAttributeSource() {
        return cacheAttributeSource;
    }

    public void setCacheAttributeSource(CacheAttributeSource cacheAttributeSource) {
        this.cacheAttributeSource = cacheAttributeSource;
    }

    public Set<LockHandlerFactory> getLockHandlerFactorySet() {
        return lockHandlerFactorySet;
    }

    public void setLockHandlerFactorySet(Set<LockHandlerFactory> lockHandlerFactorySet) {
        this.lockHandlerFactorySet = lockHandlerFactorySet;
    }

    public Set<CacheHandler> getCacheHandlerSet() {
        return cacheHandlerSet;
    }

    public void setCacheHandlerSet(Set<CacheHandler> cacheHandlerSet) {
        this.cacheHandlerSet = cacheHandlerSet;
    }

    public Map<String, RBloomFilter> getBloomFilterMap() {
        return bloomFilterMap;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}
